package com.xmhmyh.generalframework.framework.aspectj;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xmhmyh.generalframework.common.AsyncManager;
import com.xmhmyh.generalframework.common.enums.YesOrNo;
import com.xmhmyh.generalframework.common.utils.ServletUtils;
import com.xmhmyh.generalframework.framework.aspectj.annotation.Log;
import com.xmhmyh.generalframework.framework.security.LoginUser;
import com.xmhmyh.generalframework.framework.security.TokenService;
import com.xmhmyh.generalframework.framework.uid.UidGenerator;
import com.xmhmyh.generalframework.project.sys.entity.OperLog;
import com.xmhmyh.generalframework.project.sys.mapper.OperLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.TimerTask;

/**
 * @Author: 于丁
 * @DateTime: 2020/6/28 2:41 下午
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Autowired
    private TokenService tokenService;
    @Autowired
    protected UidGenerator uidGenerator;

    @Resource
    protected OperLogMapper operLogMapper;

    /**
     * @Author: 于丁
     * @Description: 请求完成后触发日志
     * @DateTime: 2020/6/28 2:51 下午
     * @Params:
     * @Return
     */
    @AfterReturning(pointcut = "com.xmhmyh.generalframework.framework.aspectj.pointCut.LogPointCut.logPointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handlerLog(joinPoint, result, null);
    }

    /**
     * @Author: 于丁
     * @Description: 请求异常后日志
     * @DateTime: 2020/6/28 2:51 下午
     * @Params:
     * @Return
     */
    @AfterThrowing(pointcut = "com.xmhmyh.generalframework.framework.aspectj.pointCut.LogPointCut.logPointCut()", throwing = "e")
    public void doAfterReturning(JoinPoint joinPoint, Exception e) {
        handlerLog(joinPoint, null, e);
    }

    /**
     * @Author: 于丁
     * @Description: 生成log
     * @DateTime: 2020/6/28 3:00 下午
     * @Params:
     * @Return
     */
    public void handlerLog(final JoinPoint joinPoint, final Object result, Exception e) {
        // 获取log注释
        Log log = getMethodAnnotation(joinPoint, Log.class);
        // 获得方法
        Method method = getMethod(joinPoint);
        // 获取request
        HttpServletRequest request = ServletUtils.getRequest();
        // 获取用户信息
        LoginUser loginUser = tokenService.getLoginUser(request);
        OperLog operLog = new OperLog();
        operLog.setId(uidGenerator.getUID());
        operLog.setOperTime(LocalDateTime.now());
        operLog.setModuleCode(log.module().getValue());
        operLog.setModuleStr(log.module().getLabel());
        operLog.setBusinessCode(log.busType().getValue());
        operLog.setBusinessStr(log.busType().getLabel());
        operLog.setMethod(method.getName());
        operLog.setRequestMethod(request.getMethod());
        operLog.setOperatorSource(log.source().getValue());
        operLog.setOperatorSourceStr(log.source().getLabel());
        operLog.setOperUserId(loginUser.getUser().getId());
        operLog.setOperName(loginUser.getUser().getName());
        operLog.setOperUrl(request.getRequestURI());
        operLog.setOperIp(ServletUtils.getIPAddress());
        String params = argsArrayToString(joinPoint.getArgs());
        operLog.setOperParam(params);
        operLog.setJsonResult(result == null ? "无返回" : JSONObject.toJSONString(result));
        operLog.setStatus(e == null ? 0 : 1);
        operLog.setErrorMsg(e == null ? "无异常" : e.getMessage());
        operLog.setEnabled(YesOrNo.YES.getValue());
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                operLogMapper.insert(operLog);
            }
        });
    }


    /**
     * @Author: 于丁
     * @Description: 获取方法
     * @DateTime: 2020/6/28 3:00 下午
     * @Params:
     * @Return
     */
    public Method getMethod(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        return methodSignature.getMethod();
    }

    /**
     * @Author: 于丁
     * @Description: 获取方法注释
     * @DateTime: 2020/6/28 3:00 下午
     * @Params:
     * @Return
     */
    public <T extends Annotation> T getMethodAnnotation(JoinPoint joinPoint, Class<T> tClass) {
        return (T) getMethod(joinPoint).getAnnotation(tClass);
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (paramsArray[i] == null) {
                    continue;
                }
                if (!isFilterObject(paramsArray[i])) {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}
